#pragma once
#include <iostream>
#include <string>
#include <queue>
#include <unistd.h>
#include <pthread.h>
using namespace std;
const static int gcap = 5;
template <typename T>
class blockqueue
{
private:
    bool isfull()
    {
        return _bq.size() >= _cap;
    }
    bool IsEmpty()
    {
        return _bq.empty();
    }


public:
    blockqueue(int _cap = gcap)
        : _cap(gcap), _c_wait_num(0), _p_wait_num(0)
    {
        pthread_mutex_init(&_lock, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
    }
    void enqueue(const T &in)
    {

        pthread_mutex_lock(&_lock);
        while (isfull())
        {
            // 1.这里为什么要在临界区之内判断 因为判断的时候本身就是访问队列这个临界资源
            // 在判断过程中，这个队列可能会发生改变，所以要保护起来
            // 2.等待的时候我们是持有锁的，所以我们要释放锁，怎么释放??
            // wait的时候自动释放锁 这也就是为什么pthread_cond_wait 为什么要传入锁的原因
            // pthread_cond_wait 可能存在 1.调用失败 2.伪唤醒的情况
            //伪唤醒——即等待队列里面没有线程却依然唤醒 解决办法加入一个等待的计数
            //
            _p_wait_num++;
            pthread_cond_wait(&_p_cond, &_lock); // 唤醒的时候自动执行以后的代码部分,然后重新竞争锁，让线程重新持有锁
            _p_wait_num--;
            //当我们被唤醒的时候，就一定又从这个位置唤醒了！
            //是在临界区内被唤醒的！！！
        }
        _bq.push(in);
        if (_c_wait_num > 0)
        {
            pthread_cond_signal(&_c_cond); // 唤醒消费者的等待队列
        }
        pthread_mutex_unlock(&_lock);
    }
    void Pop(T *out)
    {
        pthread_mutex_lock(&_lock);
        while(IsEmpty())
        {
            _c_wait_num++;
            pthread_cond_wait(&_c_cond, &_lock);
            _c_wait_num--;
        }

        *out = _bq.front();
        _bq.pop();
        if(_p_wait_num>0)
            pthread_cond_signal(&_p_cond); // 唤醒生产者
        pthread_mutex_unlock(&_lock);
    }
    ~blockqueue()
    {
        pthread_mutex_destroy(&_lock);
        pthread_cond_destroy(&_c_cond);
        pthread_cond_destroy(&_p_cond);
    }

private:
    queue<T> _bq;
    int _cap; // 容量

    pthread_mutex_t _lock;
    pthread_cond_t _c_cond;
    pthread_cond_t _p_cond;

    int _c_wait_num; // 当前消费者等待的个数
    int _p_wait_num; // 当前生产者等待的个数
};
